<template>
  <a-upload-dragger
    v-model:file-list="fileList"
    name="mp4"
    :multiple="true"
    :custom-request="customRequest"
    :before-upload="beforeUpload"
    :max-count="maxCount"
    @change="handleChange"
    @drop="handleDrop"
  >
    <div class="upload-content-box">
      <img src="@/assets/img/Vector.png" alt="" />
      <p class="upload-text">点击或拖拽上传{{ uploadText }}</p>
      <p v-if="uploadTip" class="upload-tip">{{ uploadTip }}</p>
    </div>
    <template #itemRender><span></span> </template>
  </a-upload-dragger>
  <draggableComponent
    :list="fileList"
    ghost-class="ghost"
    chosen-class="chosenClass"
    animation="300"
    item-key="uid"
    :sort="true"
    @start="onStart"
    @end="onEnd"
  >
    <template #item="{ element }">
      <div>
        <div class="item">
          <div class="item-left">
            <!-- 图标判断 -->
            <i v-if="element.status === 'error'" class="iconfont icon-close-line error-icon"></i>
            <i v-else-if="element.status === 'uploading'" class="iconfont icon-loader-4-fill loader-icon"></i>
            <i v-else class="iconfont icon-check-line item-left-icon"></i>

            <div class="item-name" :class="[element.status]">
              <p class="name" :title="element.name">{{ element.name }}</p>
              <p class="message" :title="element.message ? element.message : element.name">
                {{ element.message ? element.message : element.name }}
              </p>
            </div>
          </div>
          <div class="iconfont icon-delete-bin-line delete-icon" style="display: none" @click.stop="deleteFile(element)"></div>
        </div>
        <div v-if="element.status === 'uploading'" class="progress-class">
          <a-progress :percent="element.percent" />
        </div>
      </div>
    </template>
  </draggableComponent>
  <div class="mt-8">{{ fileList?.length }}/{{ maxCount }}条</div>
</template>
<script lang="ts" setup>
  import { Form, UploadChangeParam, UploadProps } from 'ant-design-vue'
  import { message } from 'ant-design-vue'
  import { axios, getUnixFileName } from 'g6-fn'
  import { computed, onUnmounted, ref, watch } from 'vue'
  import draggableComponent from 'vuedraggable'

  import { minioApi } from '@/services/api'
  import { http } from '@/services/http'
  import { countStrToBit } from '@/utils'

  export interface FileListType {
    fileName?: string
    id?: number
    size?: number
    url: string
    status: string
    name: string
  }

  export interface TagModalProp {
    maxCount?: number
    maxFileSize?: number
    accept?: string
    uploadText?: string
    fileUrl: FileListType[]
    uploadTip?: string
    uploading?: boolean
    docType?: string[]
  }

  const formItemContext = Form.useInjectFormItemContext()

  const props = withDefaults(defineProps<TagModalProp>(), {
    maxCount: 10,
    maxFileSize: -1,
    accept: 'mp4',
    uploadText: '视频',
    uploading: false,
    fileUrl: () => [],
    docType: () => ['pdf', 'ppt', 'pptx', 'doc', 'docx']
  })

  const emits = defineEmits<{
    (e: 'update:fileUrl', params: FileListType[]): void
    (e: 'update:uploading', params: boolean): void
  }>()

  const fileList = ref<UploadProps['fileList'] & FileListType[]>([])

  const getIsUploading = computed(() => {
    const isUploading = fileList.value.some(item => item.status === 'uploading') || false
    emits('update:uploading', isUploading)
    return isUploading
  })

  // 删除字段
  const deleteFileName = ref<string>('')
  const deleteId = ref<string>('')

  const handleChange = (info: UploadChangeParam) => {
    try {
      const status = info.file.status
      if (status === 'done') {
        message.success(`${info.file.name} file uploaded successfully.`)
      } else if (status === 'error') {
        message.error(`${info.file.name} file upload failed.`)
      }
    } catch (error: any) {
      console.log(error, 'error')
    }
  }
  function handleDrop(e: DragEvent) {
    console.log(e)
  }

  const beforeUpload = (file: any) => {
    const fileInfo = fileList.value.find(f => f.name === file.name)
    if (fileInfo) {
      file.status = 'error'
      file.message = '文件名重复'
      return Promise.reject()
    }

    if (props.maxFileSize > 0) {
      const isLt2M = file.size! / 1024 / 1024 < props?.maxFileSize
      if (!isLt2M) {
        file.status = 'error'
        file.message = `超出文件大小`
        return Promise.reject()
      }
    }

    // 控制文件类型
    const postfix: string = file?.name?.split('.')?.slice(-1)[0]
    if (!props?.accept?.split(',')?.includes(postfix)) {
      file.status = 'error'
      file.message = `格式错误`
      return Promise.reject()
    }

    // 控制文件名大小
    if (countStrToBit(file.name) > 200) {
      file.status = 'error'
      file.message = '文件名超出长度'
      return Promise.reject()
    }

    if (props.fileUrl.length > props.maxCount) {
      return Promise.reject()
    }
    return Promise.resolve()
  }

  // 自定义上传
  const customRequest = async (data: { file: any; onProgress?: any; onSuccess?: any; onError?: any }) => {
    // 1.将文件名称上传给服务端
    // 2.调后端接口响应参数的接口
    // 3.将图片渲染到页面上
    const { file, onProgress } = data
    const fileName = getUnixFileName(file.name)

    try {
      // 上传文件后缀
      let suffix = file?.name?.split('.')?.slice(-1)[0]
      let isDoc = props?.docType.includes(suffix)
      const res = await minioApi.upload('/spa/mark' + fileName, isDoc ? 'doc' : suffix)
      let source = axios.CancelToken.source()
      file.cancelRequest = source.cancel
      const config = {
        timeout: 300000, //设置超时时长
        onUploadProgress: (Progress: { loaded: number; total: number }) => {
          let percents = Math.round((Progress.loaded * 100) / Progress.total)
          // 更新进度条
          onProgress({ percent: percents }, file)
          // 更新进度条
          file.progress = percents > 99 ? 99 : percents
        },
        cancelToken: source.token
      }
      await http.upload(res.data, file, config)

      if (props.accept === 'mp4') {
        // 视频时长
        let urlTs = URL.createObjectURL(file) //获取录音时长
        let audioElementTs = new Audio(urlTs) //audio也可获取视频的时长
        let duration = 0
        audioElementTs.addEventListener('loadedmetadata', () => {
          duration = parseInt(audioElementTs.duration as any) //时长为秒，取整
          fileList.value?.forEach((item: any) => {
            if (item.uid === file.uid) {
              file.progress = 100
              item.status = 'successful'
              item.id = ''
              item.size = file.size
              item.fileName = file.name
              item.url = '/spa/mark' + fileName
              item.videoDuration = convertSecondsToMMSS(duration)
            }
          })
          emits('update:fileUrl', fileList.value)
          formItemContext.onFieldChange()
        })
        audioElementTs.addEventListener('error', () => {
          fileList.value?.forEach((item: any) => {
            if (item.uid === file.uid) {
              item.status = 'error'
              item.message = '上传失败'
            }
          })
          emits('update:fileUrl', fileList.value)
          formItemContext.onFieldChange()
        })
      } else {
        fileList.value?.forEach((item: any) => {
          if (item.uid === file.uid) {
            file.progress = 100
            item.status = 'successful'
            item.id = ''
            item.size = file.size
            item.fileName = file.name
            item.url = '/spa/mark' + fileName
          }
        })
        emits('update:fileUrl', fileList.value)
        formItemContext.onFieldChange()
      }
    } catch (error) {
      fileList.value?.forEach((item: any) => {
        if (item.uid === file.uid) {
          item.status = 'error'
          item.message = '上传失败'
        }
      })
      emits('update:fileUrl', fileList.value)
      formItemContext.onFieldChange()
    }
  }

  //拖拽开始的事件
  const onStart = () => {}

  //拖拽结束的事件
  const onEnd = () => {
    // 结束拖拽之后将数据顺序对应
    emits('update:fileUrl', fileList.value)
  }

  const deleteFile = (e: any) => {
    e.cancelRequest?.()
    deleteFileName.value = e.name
    deleteId.value = e.uid ? e.uid : e.id
    fileList.value?.forEach((item: any, index: number) => {
      if (deleteId.value === item.uid) {
        fileList.value?.splice(index, 1)
      }
    })
    emits('update:fileUrl', fileList.value)
    formItemContext.onFieldChange()
  }

  // 监听传入
  let getPropsFileUrl = watch(
    () => props.fileUrl,
    () => {
      if (!props.fileUrl) return
      let obj = props.fileUrl.map((item: any) => {
        delete item['type']
        return {
          ...item,
          id: item.id ? item.id : '',
          name: item.fileName ? item.fileName : item.name,
          url: item.url
        }
      })
      fileList.value = obj
    },
    {
      deep: true
    }
  )
  // 时间转换
  function convertSecondsToMMSS(seconds: number) {
    const minutes = Math.floor(seconds / 60) // 计算分钟数
    const remainingSeconds = seconds % 60 // 计算剩余的秒数

    const minutesString = String(minutes).padStart(2, '0') // 将分钟数转换为两位数的字符串
    const secondsString = String(remainingSeconds).padStart(2, '0') // 将秒数转换为两位数的字符串

    return `${minutesString}:${secondsString}` // 返回格式为 "mm:ss" 的字符串
  }
  onUnmounted(() => {
    getPropsFileUrl()
  })

  defineExpose({
    getIsUploading: () => getIsUploading.value
  })
</script>
<style scoped lang="less">
  .test {
    height: 50px;
  }

  .upload-content-box {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 88px;
    flex-direction: column;
  }

  .upload-text {
    margin-top: 8px;
    text-align: center;
    color: rgba(0, 0, 0, 0.75);
    .font12();
  }

  .upload-tip {
    font-size: 12px;
    color: @text04;
    font-weight: 400;
    // line-height: 20px;
  }

  .itxst {
    display: flex;
    width: 600px;
  }

  .itxst > div:nth-of-type(1) {
    flex: 1;
  }

  .itxst > div:nth-of-type(2) {
    padding-left: 20px;
    width: 270px;
  }

  .item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 4px 8px;
    margin: 8px 0;
    border-radius: 4px;

    &:active {
      cursor: all-scroll;
    }

    .item-left {
      display: flex;
      width: 95%;
      gap: 12px;

      > i {
        font-size: 22px;
      }

      .item-left-icon {
        width: 22px;
        height: 22px;
        color: green;
      }

      .item-name {
        display: flex;
        width: 90%;
        color: @text01;
        gap: 0 12px;
        .font14();

        > p {
          .elliptical-1();

          &:nth-child(1) {
            flex: 1;
            display: block;
          }

          &:nth-child(2) {
            display: none;
            width: 100px;
          }
        }

        &.error {
          color: red;
        }
      }
    }

    &:hover {
      cursor: move;
      background-color: @mask1;

      .delete-icon {
        display: block !important;
        cursor: pointer;
      }

      .item-name {
        p.name {
          display: none;
        }

        p.message {
          display: block;
        }
      }
    }
  }

  .item + .item {
    margin-top: 10px;
  }

  .ghost {
    border: solid 1px rgb(19, 41, 239);
  }

  .chosenClass {
    background-color: #f1f1f1;
  }

  .error-icon {
    width: 22px;
    height: 22px;
    color: red;
  }
  .donghua(@DHname) {
    @keyframes @DHname {
      0% {
        transform: rotate(0deg);
      }

      100% {
        transform: rotate(360deg);
      }
    }
  }

  .donghua(myDongHua); // 名称
  .animation(@animation-name,@animation-duration,@animation-timing-function) {
    animation: @arguments;
  }

  .loader-icon {
    .animation(myDongHua,4s,infinite);

    color: rgba(24, 104, 233, 1);
  }

  .progress-classP {
    height: 20px;
    line-height: 20px;
  }

  :deep(.ant-progress-bg) {
    height: 3px !important;
  }
</style>
